<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
 * FormativeLab
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		FormativeLab
 * @author		FormativeLab Dev Team
 * @copyright	Copyright (c) 2011 - 2013, FormativeLab, Inc.
 * @license		http://www.formativesoft.com/license/
 * @link		http://www.formativesoft.com/
 * @since		Version 2.0
 * @filesource ./application/core/FSL_Model.php
 */

// ------------------------------------------------------------------------

/**
 * FormativeLab FSL_Model Model
 *
 * @package		FormativeLab
 * @subpackage	core
 * @category	core
 * @author		Formative Soft Ltd
 * @link		http://www.formativesoft.com/
 */

// ------------------------------------------------------------------------

class FSL_Model extends CI_Model {

	/**
	 * User Authenication, Authorization and Tracking Information
	 * properties for all standard entity / model
	 **/
	public $registerUserId;
	public $registerDate;
	public $revisionUserId;
	public $revisionDate;
	public $revision;
	
	public $registerUser;
	public $revisionUser;
	
	/**
	 * Injecting/Encapsulate sub/child class instance reference into super
	 * class to perform different CRUD operation and event handling
	 **/
	private $entity;
	
	/**
	 * Swaping validation message of domain/model/entity objects properties
	 * use for peformaing validation messages during CRUD operation
	 **/
	private $messages = array();
	
	
	/**
	 * On Ajax request domain/model/entity objects will render as json object or 
	 * array. After having an CRUD operation jsonRender private property will 
	 * work as indicator either it will return data or json response.
	 **/
	private $jsonRender	= false;

	/**
	 * Databse standard time zone for setting. Thise feature only for
	 * MySQL database usage.
	 **/
	const TIME_ZONE   = "SET time_zone='+6:00'";
	const FETCH_LIMIT = 100;
	
	/**
	 * User Authenication, Authorization and Tracking Information
	 * mapping for all standard entity / model
	 **/
	static $mapping = array(
		'registerUserId' => array(
			'name'	   => 'k_register_user',
			'jsname'   => 'registerUserId',
			'jstype'   => 'int',
			'nullable' => false
		),
		'registerDate' => array(
			'name'	   => 'f_register_date',
			'jsname'   => 'registerDate',
			'jstype'   => 'date',
			'nullable' => false
		),
		'revisionUserId' => array(
			'name'	   => 'k_revision_user',
			'jsname'   => 'revisionUserId',
			'jstype'   => 'int',
			'nullable' => true
		),
		'revisionDate' => array(
			'name'	   => 'f_revision_date',
			'jsname'   => 'revisionDate',
			'jstype'   => 'date',
			'nullable' => true
		),
		'revision' => array(
			'name'	   => 'f_revision',
			'jsname'   => 'revision',
			'jstype'   => 'int',
			'nullable' => false
		)
	);
	
	/**
	 * User Authenication, Authorization and Tracking
	 * Information for all standard entity / model
	 **/
	static $belongsTo	= array(
		'registerUser'	=> array(
			'clazz' 	=> 'M00I001',
			'column' 	=> 'k_register_user',
			'mapedBy'	=> NULL
		),
		'revisionUser'	=> array(
			'clazz' 	=> 'M00I001',
			'column' 	=> 'k_revision_user',
			'mapedBy'	=> NULL
		)
	);

	/**
	 * Intentional declaration for hasMany association
	 * Maybe override in sub class.
	 **/
	static $hasMany		= array();
		
	/**
	 * Intentional declaration for hasOne association
	 * Maybe override in sub class.
	 **/
	static $hasOne		= array();
	
	/**
	 * Intentional declaration for performing validation
	 * Maybe override in sub class.
	 **/
	static $constraints	= array();
	
	/**
	 * To load dependent model automatically during initialization of domain
	 * /entity/model.
	 **/
	static $models		= array();
	
	/**
	 * __construct() Method is an empty constructor. This method use for 
	 * initialization of class
	 *
	 * Returns void
	 *
	 * @access	public
	 * @return  void 
	 **/
    public function __construct(){
        parent::__construct();
		$this->setTimeZone();
    }
		
	/**
	 * setTimeZone() contains no parameter. This Method is use for
	 * setting time zone before crud operation perform. This method
	 * changes the mysql client environments time zone.
	 *
	 * Returns void
	 *
	 * @access	private
	 * @return  void 
	 **/
	private function setTimeZone(){
		$this->db->query(self::TIME_ZONE);
	}
	
	/**
	 * setEntity() contains an required parameter. This pramater hold the
	 * reference of child class. This class use for instanciate private
	 * property of this class named entity. 
	 *
	 * Returns void
	 *
	 * @access	protected
	 * @param	entity
	 * @return  void 
	 **/
	protected function setEntity(&$entity){
		$this->entity =&$entity;
		loadModel(self::$models);
	}
	
	/**
	 * setJsonRender() without an parameter. This method use for enable or 
	 * disable json render directly after completing any CRUD operation
	 *
	 * @access	protected
	 * @return  void 
	 **/
	protected function setJsonRender($jsonRender = true){
		$this->jsonRender = $jsonRender;		
	}
	
	/**
	 * getJsonRender() without any parameters. This method use for enable 
	 * or disable json render directly after completing any CRUD operation
	 *
	 * @access	public
	 * @return  void 
	 **/
	protected function getJsonRender(){
		return $this->jsonRender;
	}
		
	/**
	 * getMapping() contains no parameter. This Method is use for
	 * get static mapping property. This method returns merge mapping
	 * of child and parent mapping properties associative array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getMapping(){
		$entity		= &$this->entity;
		$tracking	= self::$mapping;
		$mapping	= $entity::$mapping;				
		return array_merge($mapping, $tracking);
	}
	
	/**
	 * getBelongsTo() contains no parameter. This Method is use for
	 * get static belongsTo property. This method returns merge belongsTo
	 * of child and parent belongsTo properties as associative array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getBelongsTo(){
		$entity				= &$this->entity;
		$parentBelongsTo	= self::$belongsTo;
		$clazz				= get_class($entity);		
		$hasProperty		= belongsToPropertyExists($clazz);
		$belongsTo			= $hasProperty? $clazz::$belongsTo : array();		
		return array_merge($belongsTo, $parentBelongsTo);
	}
	
	/**
	 * getHasMany() contains no parameter. This Method is use for
	 * get static hasMany property. This method returns merge hasMany
	 * of child and parent hasMany properties as associative array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getHasMany(){
		$entity				= &$this->entity;
		$parentHasMany		= self::$hasMany;
		$clazz				= get_class($entity);		
		$hasProperty		= hasManyPropertyExists($clazz);
		$hasMany			= $hasProperty? $clazz::$hasMany : array();		
		return array_merge($hasMany, $parentHasMany);
	}
	
	/**
	 * getHasOne() contains no parameter. This Method is use for
	 * get static hasOne property. This method returns merge hasOne
	 * of child and parent hasOne properties as associative array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getHasOne(){
		$entity				= &$this->entity;
		$parentHasOne		= self::$hasOne;
		$clazz				= get_class($entity);		
		$hasProperty		= hasOnePropertyExists($clazz);
		$hasOne				= $hasProperty? $clazz::$hasOne : array();		
		return array_merge($hasOne, $parentHasOne);
	}
	
	/**
	 * getConstraints() contains no parameter. This Method is use for
	 * get static constraints property. This method returns merge 
	 * constraints of child and parent constraints properties as associative
	 * array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getConstraints(){
		$entity				= &$this->entity;
		$parentConstraints	= self::$constraints;
		$clazz				= get_class($entity);		
		$hasProperty		= constraintsPropertyExists($clazz);
		$constraints		= $hasProperty? $clazz::$constraints : array();		
		return array_merge($constraints, $parentConstraints);
	}
	
	/**
	 * getModels() contains no parameter. This Method is use for
	 * get static models property. This method returns merge 
	 * models of child and parent models properties as associative
	 * array.
	 *
	 * Returns associative array
	 *
	 * @access	public
	 * @return  array 
	 **/
	public function getModels(){
		$entity				= &$this->entity;
		$parentModels		= self::$models;
		$clazz				= get_class($entity);		
		$hasProperty		= modelsPropertyExists($clazz);
		$models				= $hasProperty? $clazz::$models : array();		
		return array_merge($models, $parentModels);
	}
	
	/**
	 * decode() contains no parameter. This Method is use for
	 * entity convert object to associative array with mapping.
	 *
	 * Returns associative array
	 *
	 * @access	protected
	 * @return  array 
	 **/
	protected function decode(){
		return decodeModel($this->entity);
	}
	
	/**
	 * encode() contains single parameter of standard class object
	 * its convert standard class object to standard Entity object
	 *
	 * Returns object
	 *
	 * @access	protected
	 * @param	data
	 * @return  object 
	 **/
	protected function encode(&$data){
		$entity 	= $this->entity;
		return encodeModel($entity, $data);
	}
		
	/**
	 * beginTrasaction() without any parameter. Use for RDBMS managed
	 * transaction begin
	 *
	 * @access	public
	 * @return  void 
	 **/
	public function beginTrasaction(){
		$entity = &$this->entity;
		$entity->db->trans_begin();
	}
	
	/**
	 * startTrasaction() without any parameter. Use for RDBMS managed
	 * transaction start
	 *
	 * @access	public
	 * @return  void 
	 **/
	public function startTrasaction(){
		$entity = &$this->entity;
		$entity->db->trans_start();
	}
	
	/**
	 * rollbackTrasaction() without any parameter. Use for RDBMS managed
	 * transaction rollback
	 *
	 * @access	public
	 * @return  void 
	 **/
	public function rollbackTrasaction(){
		$entity = &$this->entity;
		$entity->db->trans_rollback();
	}
	
	/**
	 * commitTrasaction() without any parameter. Use for RDBMS managed
	 * transaction commit
	 *
	 * @access	public
	 * @return  void 
	 **/
	public function commitTrasaction(){
		$entity = &$this->entity;
		$entity->db->trans_complete();
	}
	
	/**
	 * savedEntity() with no parameters used for retrieve standard 
	 * persis object from database
	 *
	 * Returns object
	 *
	 * @access	private
	 * @return  object 
	 **/
	private function savedEntity(){
		$entity		= &$this->entity;
		$id			= $entity->db->insert_id();
		$model		= $entity->findById($id);		
		return $model;
	}
	
	/**
	 * updatedEntity() with no parameters used for retrieve standard 
	 * persis object from database
	 *
	 * Returns object
	 *
	 * @access	private
	 * @return  object 
	 **/
	private function updatedEntity($id){
		$entity		= &$this->entity;
		$model		= $entity->findById($id);		
		return $model;
	}
	
	/**
	 * mergeEntity() with an required parameter of entity standard 
	 * entity class object use for merge value of persis object
	 *
	 * Returns object
	 *
	 * @access	private
	 * @param	model
	 * @return  object 
	 **/
	private function mergeEntity(&$model){
		$entity		= &$this->entity;
		$identity	= getIdProperty($model);
		$id			= $model->{$identity};
		$persis		= $id ? $entity->updatedEntity($id) : $entity->savedEntity();
		return mergeEntity($model, $persis);
	}
	
	/**
	 * restore() with an required parameters used for restore standard entity 
	 * object its use for perform restore during beforeInsert, beforeSelect,
	 * beforeUpdate or beforeDelete operation faluire.
	 *
	 * Returns object
	 *
	 * @access	private
	 * @param 	pphase
	 * @return  object 
	 **/
	private function restore($pphase){		
		$entity		= &$this->entity;
		mergeEntity($entity, $pphase);
		$this->jsonRender($entity, 1, false);
		return $entity;
	}
	
	/**
	 * jsonRender() contains three input parameters. First two parameters are mandatory
	 * and last one is optional. This Method is usefull for conditional render with
	 * Extjs 4.1 data package JSON response.
	 *
	 * Returns void.
	 *
	 * @access	protected
	 * @param	records
	 * @param	count
	 * @param	success
	 * @return  void 
	 **/
	protected function jsonRender($records, $count, $success=true){
		if($this->getJsonRender()){
			$messages	= $this->messages;
			$data		= $this->getResponseData($records, $count, $success, $messages);
			$this->render->jsonData($data);
		}
	}
	
	/**
	 * loadLanguage() is an function without any parameters. This method is use for
	 * internationalizaition messaging for validation message
	 *
	 * @access		private
	 * @return		void
	 **/
	protected function setLocal($local = 'english'){
		$entity			= &$this->entity;
		$class			= get_class($entity);
		loadLanguage($class, $local);
	}
	
	/**
	 * buildWhere() with an optional parameters used for build criteria of
	 * standard entity. Reuseable criteria builder
	 *
	 * @access	private
	 * @param 	where
	 * @return  void 
	 **/
	private function buildWhere(Array $where = array()){
		$entity		= &$this->entity;
		
		foreach($where as $property => $compare){
			$field		= getFieldByProperty($entity, $property);
			$operator	= $compare['operator'];
			$value		= $compare['value'];
			switch($operator){
				case 'eq'			: $entity->db->where($field, $value); 								break;
				case 'orEq'			: $entity->db->or_where($field, $value); 							break;
				case 'like' 		: $entity->db->like($field, $value, $compare['match']); 			break;
				case 'orLike' 		: $entity->db->or_like($field, $value, $compare['match']); 			break;
				case 'notLike' 		: $entity->db->not_like($field, $value, $compare['match']); 		break;
				case 'orNotLike' 	: $entity->db->or_not_like($field, $value, $compare['match']); 		break;
				case 'in'			: $entity->db->where_in($field, $value); 							break;
				case 'orIn'			: $entity->db->or_where_in($field, $value); 						break;
				case 'notIn'		: $entity->db->where_not_in($field, $value); 						break;
				case 'orNotIn'		: $entity->db->or_where_not_in($field, $value); 					break;
			}
		}
	}
	
	/**
	 * buildJoin() with an optional parameters used for build join of
	 * standard entity. Reuseable join builder
	 *
	 * @access	private
	 * @param 	join
	 * @return  void 
	 **/
	private function buildJoin(Array $join = array()){
		$entity		= &$this->entity;
		
		foreach($join as $table => $condition){
			$entity->db->join($table, $condition);
		}
	}
	
	/**
	 * validate() with an optional parameter used for validate standard entity 
	 * object its use for perform validation during create and update operation.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @param	skip
	 * @return  bool 
	 **/
	protected function validate($skip = array()){		
		$entity		= &$this->entity;
		//print_r($entity);
		$validator	= &Constraints::init($entity, $skip);
		if(!$validator->validate()){
			$entity->messages	= &$validator->getMessages();
			return false;
		}
		return true;
	}
	
	/**
	 * clean() with no parameters used for empty a table. helpfull for
	 * clean all data inside table.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @return  bool 
	 **/
	protected function clean(){
		$entity		= $this->entity;
		$this->db->empty_table($entity::ENTITY);
		return true;
	}
	
	/**
	 * truncate() with no parameters used for truncate a table. helpfull for
	 * clean all data and metadata inside table.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @return  bool 
	 **/
	protected function truncate(){
		$entity		= $this->entity;
		$this->db->truncate($entity::ENTITY);
		return true;
	}
	
	/**
	 * total() with no parameters used for count no.of records in a table.
	 * helpfull to detect either has records or empty.
	 *
	 * Returns integer
	 *
	 * @access	protected
	 * @return  integer 
	 **/
	protected function total(){
		$clazz		= get_class($this->entity);
        $count 		= $this->db->count_all($clazz::ENTITY);
        return $count;
    }
	
	/**
	 * count() with two parameters used for count no.of records in a table 
	 * fetch by the query. Helpfull to detect on.of records fetch by the 
	 * query.
	 *
	 * Returns integer
	 *
	 * @access	protected
	 * @param	where
	 * @param	join
	 * @return  integer 
	 **/
	protected function count(Array $where= array(), Array $join= array()){
		$entity		= &$this->entity;
		
		$entity->db->select('*');
		$entity->db->from($entity::ENTITY);
		
		$this->buildWhere($where);
		$this->buildJoin($join);
		
        $count 	= $entity->db->count_all_results();
        return $count;
    }
	
	/**
	 * hasChild() with no parameters used for audit an entity object
	 * either its has child record or not. Usefull to perform pre 
	 * delete operation.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @return  bool 
	 **/
	protected function hasChild(){
		$clazz		= get_class($this->entity);
		return false;
	}
	
	/**
	 * isDirty() with an required parameter of entity standard  entity class object
	 * use for merge value of persis object
	 *
	 * Returns object
	 *
	 * @access	private
	 * @param	model
	 * @return  bool 
	 **/
	private function isDirty(&$model){
		$entity		= &$this->entity;
		if(!isDirtyEntity($entity, $model)){
			$className		= strtolower(get_class($entity));
			$languageKey	= "{$className}.dirty.success";
			$messages 		= $this->lang->line($languageKey);
			$this->messages = parseUnicodedHexString($messages);
			return false;
		}
		return true;
	}
	
	/**
	 * save() does not required any parameter. This method use for
	 * persis entity object. Return true for perform insert operation 
	 * successfully.
	 *
	 * @access	public
	 * @return  bool 
	 **/
	public function save($jsonRender = false){
		$this->setJsonRender($jsonRender);
		$entity		= &$this->entity;		
		$pphase		= clone $entity;		
		$event		= FslCrudEvent::init($entity, 'beforeInsert');		
		
		if($entity->beforeInsert($event)){
			$data 	= $entity->decode();			
			$entity->db->set($data);
			$entity->db->insert($entity::ENTITY);
			$entity->mergeEntity($entity);			
			$event	= FslCrudEvent::init($entity, 'afterInsert');	
			$entity->afterInsert($event);
			return $entity;
		}else{
			return $entity->restore($pphase);
		}
    }
	
	/**
	 * query() four optional parameters. This method use for build query
	 * to retrieve persis objects according to criteria 
	 *
	 * @access	protected
	 * @param	where
	 * @param	join
	 * @param	limit
	 * @param	offset
	 * @return  object 
	 **/
	protected function query(Array $where= array(), Array $join= array(), $limit = self::FETCH_LIMIT, $offset = 0){
		$entity		= &$this->entity;
		$data		= array(
			'where'	=> &$where,
			'join'	=> &$join
		);
		$event		= FslCrudEvent::init($data, 'beforeSelect');
		
		if($entity->beforeSelect($event)){
			$entity->db->select('*');
			$entity->db->from($entity::ENTITY);	
		
			$this->buildWhere($where);
			$this->buildJoin($join);
			
			$entity->db->limit($limit, $offset);
			$query    = $entity->db->get();
		}
		return $query;
	}
	
	/**
	 * get() required single parameter. This method use for get persis
	 * object from databse table.Returns entity object from database 
	 *
	 * @access	public
	 * @param	id
	 * @return  object 
	 **/
	public function get($id = null){
		$entity		= &$this->entity;
		$id			= $id ? $id : $entity->id;
		$property 	= getIdProperty($entity);
		
		if(isset($id)){
			$id			= is_numeric($id) ? $id : $this->db->escape($id);
			$where		= array(
				$property => array(
					'operator'		=> 'eq',
					'value'			=> $id
				)
			);
			$join		= array();
		
			$query		= $entity->query($where, $join, 1, 0);
			$model		= $entity->encode($query->row());
		}
		$event	= FslCrudEvent::init($model, 'afterSelect');		
		$entity->afterSelect($event);
		return $model;
	}
	
	/**
	 * findById() required single parameter. This method use for get persis
	 * object from databse table. Returns entity object from database 
	 *
	 * @access	public
	 * @param	id
	 * @return  object 
	 **/
	public function findById($id = null){
		$entity		= &$this->entity;
		$model		= $entity->get($id);
		return $model;
	}
	
	/**
	 * findWhere() an optional parameter. This method use for 
	 * retrieve persis objects according to criteria
	 *
	 * @access	public
	 * @param	where
	 * @return  object 
	 **/
	public function findWhere(Array $where = array(), Array $join= array()){
		$entity		= &$this->entity;		
		$query		= $entity->query($where, $join, 1, 0);
		$model		= $entity->encode($query->row());
		
		return $model;
    }
	
	/**
	 * findAllWhere() three optional parameters. This method use for 
	 * retrieve persis objects according to criteria
	 *
	 * @access	public
	 * @param	where
	 * @param	limit
	 * @param	offset
	 * @return  array of object 
	 **/
	public function findAllWhere(Array $where= array(), Array $join= array(), $limit = self::FETCH_LIMIT, $offset = 0){
		$list  		= array();
		$entity		= &$this->entity;		
		$query		= $entity->query($where, $join, $limit, $offset);
		
		foreach($query->result() as $key => $val){
			$model  = $this->encode($val);
			$list[] = $model;
		}
        return $list;
    }
	
	/**
	 * update() does not required any parameter. This method use for
	 * delete entity object. Return true for perform update operation 
	 * successfully.
	 *
	 * @access	public
	 * @return  bool 
	 **/
	public function update($jsonRender = false){
		$this->setJsonRender($jsonRender);
		$entity		= &$this->entity;
		$pphase		= clone $entity;
		$event		= FslCrudEvent::init($entity, 'beforeUpdate');	
		
		if(isset($entity->id)){
			$model = $entity->get($entity->id);
			if(isset($model)){
				$entity->revision = $model->revision;
				if($this->isDirty($model) && $entity->beforeUpdate($event)){
					$data = $entity->decode();
					
					$entity->db->where($entity::ID_FIELD, $entity->id);				
					$entity->db->update($entity::ENTITY, $data);
					$entity->mergeEntity($entity);
					
					$event	= FslCrudEvent::init($entity, 'afterUpdate');
					$entity->afterUpdate($event);
					return $entity;
				}					
			}
		}
		return $entity->restore($pphase);
	}
	
	/**
	 * destory() does not required any parameter. This method use for
	 * delete entity object. Return true for perform delete operation 
	 * successfully.
	 *
	 * @access	public
	 * @return  bool 
	 **/
	public function destory($jsonRender = false){
		$this->setJsonRender($jsonRender);
		$entity		= &$this->entity;
		$pphase		= clone $entity;
		$event		= FslCrudEvent::init($entity, 'beforeDelete');	
	
		if(isset($entity->id)){
			$model = $this->get($entity->id);
			if(isset($model)){
				if($entity->beforeDelete($event)){
					$data = $entity->decode();
					$entity->db->where($entity::ID_FIELD, $entity->id);				
					$entity->db->delete($entity::ENTITY);
					
					$event	= FslCrudEvent::init($entity, 'afterDelete');
					$entity->afterDelete($event);
					return true;
				}				
			}
		}else{
			return $entity->restore($pphase);
		}
	}
	
	/**
	 * batchSave() contains an required parameters which one hold
	 * the list of entity object. this method is for batch insert
	 * of list of entity object. with no callback function implemented
	 * yet. Return true for perform batch update successfully.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @param   batch
	 * @return  bool 
	 **/
	protected function batchSave(&$batch){
		$data		= array();
		$clazz		= get_class($this->entity);
		
		foreach($batch as $index => &$model){
			if($clazz == get_class($model)){
				$data[] = $model->decode();
			}
		}
		$this->db->insert_batch($clazz::ENTITY, $data);
		return true;
	}
	
	/**
	 * batchUpdate() contains an required parameters which one hold
	 * the list of entity object. this method is for batch update
	 * of list of entity object. with no callback function implemented
	 * yet. Return true for perform batch update successfully.
	 *
	 * Returns bool
	 *
	 * @access	protected
	 * @param	batch
	 * @return  bool 
	 **/
	protected function batchUpdate(&$batch){
		$data 		= array();
		$clazz		= get_class($this->entity);
		
		foreach($batch as $index => $model){
			if($clazz == get_class($model)){
				$data[] = $model->decode();
			}
		}
		$this->db->update_batch($clazz::ENTITY, $data, 'f_name'); 
	}
	
	/**
	 * beforeInsert() Method contains single required parameter.
	 * This method use for Before Insert/Save Callback handler
	 *
	 * Returns bool value
	 *
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	public function beforeInsert(FslCrudEvent &$event){
		$entity					= &$event->data;
		$entity->registerUserId	= 1;
		$entity->registerDate	= new DateTime();
		$entity->revision		= 0;
		
		unset($entity->revisionUserId);
		unset($entity->revisionDate);
		
		return $entity->validate();
	}
	
	/**
	 * beforeSelect() Method contains single required parameter.
	 * This method use for Before Select/Search Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	public function beforeSelect(FslCrudEvent &$event){
		$data					= &$event->data;
		$where					= &$data['where'];
		$join					= &$data['join' ];
		return true;
	}
	
	/**
	 * beforeUpdate() Method contains single required parameter.
	 * This method use for Before Update/Edit Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	public function beforeUpdate(FslCrudEvent &$event){	
		$entity					= &$event->data;
		$entity->revisionUserId	= 1;
		$entity->revisionDate	= new DateTime();
		$entity->revision		= ($entity->revision ? $entity->revision : 0) + 1;
		
		unset($entity->registerUserId);
		unset($entity->registerDate);
		
		return $entity->validate();
	}
	
	/**
	 * beforeDelete() Method contains single required parameter.
	 * This method use for Before Delete/Destroy Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool
	 **/
	public function beforeDelete(FslCrudEvent &$event){
		
		return true;
	}
	
	/**
	 * afterInsert() Method contains single required parameter.
	 * This method use for After Insert/Save Event Callback handler
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	public function afterInsert(FslCrudEvent &$event){
		$entity			= &$event->data;
		$className		= strtolower(get_class($this->entity));
		$languageKey	= "{$className}.save.success";
		$messages 		= $this->lang->line($languageKey);
		$this->messages = parseUnicodedHexString($messages);
		$this->jsonRender($entity, 1, true);
	}
	
	/**
	 * afterSelect() Method contains single required parameter.
	 * This method use for After Select/Search Event Callback handler
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	public function afterSelect(FslCrudEvent &$event){
		$entity			= &$event->data;
		$className		= strtolower(get_class($this->entity));
		$languageKey	= "{$className}.query.success";
		//echo $languageKey;
		$messages 		= $this->lang->line($languageKey);
		$this->messages = parseUnicodedHexString($messages);
	}
	
	/**
	 * afterUpdate() Method contains single required parameter.
	 * This method use for Update/Edit Event Callback handler
	 *
	 * Returns void
	 * 
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	public function afterUpdate(FslCrudEvent &$event){
		$entity			= &$event->data;
		$className		= strtolower(get_class($this->entity));
		$languageKey	= "{$className}.update.success";
		$messages 		= $this->lang->line($languageKey);
		$this->messages = parseUnicodedHexString($messages);
		$this->jsonRender($entity, 1, true);
	}
	
	/**
	 * afterDelete() Method contains single required parameter.
	 * This method use for Delete/Destroy Event Callback handler
	 *
	 * Returns void
	 * 
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	public function afterDelete(FslCrudEvent &$event){
		$entity			= &$event->data;
		$className		= strtolower(get_class($this->entity));
		$languageKey	= "{$className}.delete.success";
		$messages 		= $this->lang->line($languageKey);
		$this->messages = parseUnicodedHexString($messages);
		$this->jsonRender($entity, 1, true);
	}
	
	/**
	 * getResponseData() contains four input parameters. First two
	 * parameters are mandatory and rest of them are optinal. This
	 * Method is usefull for Extjs 4.1 data package JSON/XML response
	 * Builder.
	 *
	 * Returns mixed value contains object and array.
	 *
	 * @access	public
	 * @param	records
	 * @param	count
	 * @param	success
	 * @param	message
	 * @return  mixed 
	 **/
	public function getResponseData($records, $count, $success=true, $message=''){
		$data	= is_array($records) ? $records : array($records);
		return getResponseData($this->entity, $data, $count, $success, $message);
	}
	
}

/**
 * Basic and common CRUD operation will goes here.
 * Must be implement in custom domain class
 **/
interface Repository {
	function save();
}

/**
 * Basic Response Data for Extjs 4.1 Data response
 * specially for JSON response.
 **/
interface ResponseData {
	
	/**
	 * getResponseData() contains four input parameters. First two
	 * parameters are mandatory and rest of them are optinal. This
	 * Method is usefull for Extjs 4.1 data package JSON/XML response
	 * Builder.
	 *
	 * Returns mixed value contains object and array.
	 *
	 * @access	public
	 * @param	records
	 * @param	count
	 * @param	success
	 * @param	message
	 * @return  mixed 
	 **/
	function getResponseData($records, $count, $success=true, $message='');
}

/**
 * DataCapsule use for encapsulate data with very basic MetaData
 * Information. Specially this class use for swaping data and no.
 * of records fetch by the query. Very much use for pagination
 **/
 class DataCapsule {
	
	public $data;
	public $count;
	public $message;
	
	/**
	 * __construct() Method contains three parameters. First two are
	 * required and another one is optional. This method use for 
	 * initialization of class
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	data
	 * @param	count
	 * @param	message
	 * @return  void 
	 **/
	public function __construct(&$data, $count, $message = '') {
		$this->data		= &$data;
		$this->count 	= $count;
		$this->message	= $message;
	}
	
	/**
	 * init() Method contains three parameters. First two are
	 * required and another one is optional. This method use for 
	 * initialization of class
	 *
	 * Returns DataCapsule
	 *
	 * @access	public static
	 * @param	data
	 * @param	count
	 * @param	message
	 * @return  DataCapsule 
	 **/
	public static function init(&$data, $count, $message = ''){
		$capsule	= new DataCapsule($data, $count, $message);
		return $capsule;
	} 
 }
 
/**
 * CRUD Event for CRUD Operation
 **/
class FslCrudEvent {

	public $data;
	public $name;
	
	/**
	 * __construct() Method contains two parameters. First one is
	 * required and another one is optional. This method use for 
	 * initialization of class
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	data
	 * @param	name
	 * @return  void 
	 **/
	public function __construct(&$data, $name = ''){
		$this->data = $data;
		$this->name	= $name;
	}
	
	/**
	 * init() Method contains two parameters. First one is
	 * required and another one is optional. This method use for 
	 * initialization of class
	 *
	 * Returns DataCapsule
	 *
	 * @access	public static
	 * @param	data
	 * @param	name
	 * @return  FslCrudEvent 
	 **/
	public static function init(&$data, $name = ''){
		$event	= new FslCrudEvent($data, $name);
		return $event;
	}
}

/**
 * Before Insert/Save Event Listener
 **/
interface PreInsertListener {
	/**
	 * beforeInsert() Method contains single required parameter.
	 * This method use for Before Insert/Save Callback handler
	 *
	 * Returns bool value
	 *
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	function beforeInsert(FslCrudEvent &$event);
}

/**
 * Before Select/Search Event Listener
 **/
interface PreSelectListener {
	/**
	 * beforeSelect() Method contains single required parameter.
	 * This method use for Before Select/Search Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	function beforeSelect(FslCrudEvent &$event);
}

/**
 * Before Update/Edit Event Listener 
 **/
interface PreUpdateListener {
	/**
	 * beforeUpdate() Method contains single required parameter.
	 * This method use for Before Update/Edit Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool 
	 **/
	function beforeUpdate(FslCrudEvent &$event);
}

/**
 * Before Delete/Destroy Event Listener
 **/
interface PreDeleteListener {
	/**
	 * beforeDelete() Method contains single required parameter.
	 * This method use for Before Delete/Destroy Event Callback handler
	 *
	 * Returns bool value
	 * 
	 * @access	public
	 * @param	event
	 * @return  bool
	 **/
	function beforeDelete(FslCrudEvent &$event);
}

/**
 * After Insert/Save Event Listener
 **/
interface PostInsertListener {
	/**
	 * afterInsert() Method contains single required parameter.
	 * This method use for After Insert/Save Event Callback handler
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	function afterInsert(FslCrudEvent &$event);
}

/**
 * After Select/Search Event  Listener
 **/
interface PostSelectListener {
	/**
	 * afterSelect() Method contains single required parameter.
	 * This method use for After Select/Search Event Callback handler
	 *
	 * Returns void
	 *
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	function afterSelect(FslCrudEvent &$event);
}

/**
 * After Update/Edit Event  Listener
 **/
interface PostUpdateListener {
	/**
	 * afterUpdate() Method contains single required parameter.
	 * This method use for Update/Edit Event Callback handler
	 *
	 * Returns void
	 * 
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	function afterUpdate(FslCrudEvent &$event);
}

/**
 * After Delete/Destroy Event Listener
 **/
interface PostDeleteListener {
	/**
	 * afterDelete() Method contains single required parameter.
	 * This method use for Delete/Destroy Event Callback handler
	 *
	 * Returns void
	 * 
	 * @access	public
	 * @param	event
	 * @return  void 
	 **/
	function afterDelete(FslCrudEvent &$event);
}

/**
 * Before CRUD Event Listener
 **/
interface PreCrudListener extends PreInsertListener, PreSelectListener, PreUpdateListener, PreDeleteListener {

}

/**
 * After CRUD Event Listener
 **/
interface PostCrudListener extends PostInsertListener, PostSelectListener, PostUpdateListener, PostDeleteListener {

}

/**
 * Before and After CRUD Event Listener
 **/
interface PrePostCrudListener extends PreCrudListener, PostCrudListener {

}

/* End of file FSL_Model.php */
/* Location: ./application/core/FSL_Model.php */